// import { IPluginContext } from '@tarojs/service';
import { resolve } from 'path';
import fs = require('fs');

import { extractProjectInfo, ProjectBasicInfo } from '../../project-info';
import { filterExistFiles, loadJSON } from '../../utils';

export type CustomProjectConfigOptions = {
  /**
   * 项目自定义配置的目录，绝对路径
   */
  configRoot: string,
  /**
   * 项目的编译输出目录，绝对路径
   */
  outputRoot: string,
  filterConfig?: (config: Record<string, any>) => Record<string, any>,
  [key: string]: any,
}

type FileInfo = {
  filePath: string,
  content: string,
};

/**
 * 使项目不依赖于 @tarojs，只关注我们要用到 taro Context 的那些接口，做定义
 * 实际上还是应该要了解 IPluginContext ：https://github.com/NervJS/taro/blob/next/packages/taro-service/types/index.d.ts
 */
export interface TaroPluginContextSimplify {

  writeFileToDist(fileInfo: FileInfo): void;

  onBuildStart(fn: Function): void;

  onBuildFinish(fn: Function): void;
}

const Platforms = ['weapp', 'swan', 'alipay', 'h5', 'rn', 'tt', 'quickapp', 'qq', 'jd'];

/**
 * 插件定义
 */
export class CustomProjectConfig {

  ctx: TaroPluginContextSimplify;

  opts: CustomProjectConfigOptions;

  info: ProjectBasicInfo;

  private _isInit = false;

  private _isChanged = false;

  private _configFile = '';

  private _configData = {};

  constructor(
    ctx: TaroPluginContextSimplify,
    opts: CustomProjectConfigOptions,
  ) {
    this.ctx = ctx;
    this.opts = opts;
    this.info = extractProjectInfo(process.env);
  }

  log(type: string, sub: string, message: string): this {
    if (!message) {
      // @ts-ignore
      this.ctx.helper.printLog(type, '项目配置', sub);
    } else {
      // @ts-ignore
      this.ctx.helper.printLog(type, sub, message);
    }
    return this;
  }

  /**
   * 生成自定义配置目录下的路径的绝对路径
   *
   * @param path
   */
  configOf(path: string): string {
    return resolve(this.opts.configRoot, path);
  }

  /**
   * 生成小程序输出目录下的路径的绝对路径
   *
   * @param path
   */
  outputOf(path: string): string {
    return resolve(this.opts.outputRoot, path);
  }

  get shouldGenerateConfigFile() {
    return Platforms.indexOf(this.info.platform) > -1;
  }

  /**
   * 根据平台生成对应的配置文件名
   *
   */
  getOutputConfigFilename(platform: string): string {
    switch (platform) {
      // @todo 要补充其他平台的，暂时只针对几个特定的平台
      case 'weapp' :
        return 'project.config.json';
      case 'swan' :
        return 'project.swan.json';
    }
    this.log('warning', '项目配置', `未支持  ${platform} 平台`);
    return 'project.export.json';
  }

  get isChanged(): boolean {
    return this._isChanged;
  }

  /**
   * 生成自定义配置文件的清淡
   */
  generateConfigFiles(): string[] {
    const { mode, custom, platform } = this.info;

    // 统一输入的文件名，如：weapp.json
    const fileName = `${platform}.json`;
    const files: string[] = [];

    if (custom !== '') {
      // weapp.dev.janpoem.json
      files.push(fileName.replace(/\.json$/, `.${mode}.${custom}.json`));
      // weapp.janpoem.json
      files.push(fileName.replace(/\.json$/, `.${custom}.json`));
    }

    // weapp.dev.json
    files.push(fileName.replace(/\.json$/, `.${mode}.json`));
    // weapp.json
    files.push(fileName);

    return files.map(this.configOf.bind(this));
  }

  /**
   *
   */
  initConfig(): this {
    if (this._isInit) {
      return this;
    }
    this._isInit = true;

    // 生成存在的文件列表
    const files = filterExistFiles(this.generateConfigFiles());

    if (files.length <= 0) {
      this.log('warning', '项目配置', `未发现 ${this.info.platform} 项目配置文件`);
    } else {
      this._configFile = files[0];

      const configPath = this.configOf(this._configFile);
      this._isChanged = true;
      this._configData = loadJSON(configPath);

      fs.watchFile(configPath, () => {
        this._configData = loadJSON(configPath);

        if (this._configData === false) {
          this.log('warning', '配置信息', `加载 ${configPath} 出错`);
        } else {
          this._isChanged = true;
          this.log('modify', '修改配置', `${configPath}`);
          this.writeProjectConfig();
        }
      });

      this.log('start', '项目配置', `${configPath}`);
    }

    return this;
  }

  writeProjectConfig() {
    if (!this._isChanged) {
      return this;
    }
    let data = Object.assign({}, this._configData);
    if (typeof this.opts.filterConfig === 'function') {
      data = this.opts.filterConfig(data);
    }
    if (typeof data !== 'object') {
      data = {};
    }

    const file = this.getOutputConfigFilename(this.info.platform);
    this.ctx.writeFileToDist({
      filePath: file,
      content : JSON.stringify(data, null, 2),
    });
    this._isChanged = false;
    this.log('generate', '写入配置', `${file}`);
    return this;
  }
}
